﻿//-----------------------------------------------------------------------
// <copyright file="Application.cs" company="Alan Rodrigues">
//     Copyright (c) Alan Rodrigues. All rights reserved.
// </copyright>
// <developed>Alan Rodrigues (operninha@gmail.com)</developed>
//-----------------------------------------------------------------------

namespace Authorization
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using Microsoft.Interop.Security.AzRoles;
    using System.Linq;
    using System.Linq.Expressions;
    using Authorization.Properties;

    /// <summary>
    /// Application
    /// </summary>
    [DataContract(Name = "Application", Namespace = "http://authorizationservices/v1")]
    public class Application
    {
        #region Variables

        #endregion

        #region Constructors
    
        /// <summary>
        ///  Construtor
        /// </summary>
        /// <param name="applicationName">Name da <see cref="Application"/></param>
        /// <param name="authorizationStore">Gerente dessa <see cref="Application"/></param>
        public Application(string applicationName, AuthorizationStore authorizationStore)
        {
            SecUtility.CheckParameter(ref applicationName, true, true, false, 0, "applicationName");

            if (authorizationStore == null)
            {
                throw new ArgumentNullException("authorizationStore");
            }

            try
            {
                this.AuthorizationStore = authorizationStore;
                this.AzManApplication = (IAzApplication3)authorizationStore.Store.OpenApplication2(applicationName, null);
            }
            catch (ExternalException ex)
            {
                var error =
                    new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                throw new AuthorizationException(ex, "IAzAuthorizationStore.OpenApplication2");
            }

           // this.Name = applicationName;
          //  this.Version = this.AzManApplication.Version;
           // this.Description = this.AzManApplication.Description;
        }
        
        internal Application(IAzApplication3 azmanApplication, AuthorizationStore authorizationStore)
        {
            if (azmanApplication == null)
            {
                throw new ArgumentNullException("azmanApplication");
            }

            if (authorizationStore == null)
            {
                throw new ArgumentNullException("authorizationStore");
            }

            this.AuthorizationStore = authorizationStore;
            this.AzManApplication = azmanApplication;
           // this.Name = azmanApplication.Name;
           // this.Version = azmanApplication.Version;
            //this.Description = azmanApplication.Description;
        }
        
        #endregion
        
        #region Destructors

        /// <summary>
        /// Finalizador
        /// </summary>
        ~Application()
        {
            if (this.AzManApplication != null)
            {
                Marshal.FinalReleaseComObject(this.AzManApplication);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// The Store where the <see cref="Application"/> is stored.
        /// </summary>
        public AuthorizationStore AuthorizationStore
        {
            get;
            private set;
        }

        /// <summary>
        /// The description of the <see cref="Application"/>
        /// </summary>
        [DataMember(Name = "Description", IsRequired = false)]
        public string Description
        {
            get { return this.AzManApplication.Description; }
            set { this.AzManApplication.Description = value; }
        }

        /// <summary>
        /// The name of the <see cref="Application"/>
        /// </summary>
        [DataMember(Name = "Name", IsRequired = true)]
        public string Name
        {
            get { return this.AzManApplication.Name; }
            set { this.AzManApplication.Name = value; }
        }

        /// <summary>
        /// The version of the <see cref="Application"/>
        /// </summary>
        [DataMember(Name = "Version", IsRequired = false)]
        public string Version
        {
            get { return this.AzManApplication.Version; }
            set { this.AzManApplication.Version = value; }
        }

        /// <summary>
        /// The Extended Data of the <see cref="Application"/>
        /// </summary>
        [DataMember(Name = "ApplicationData", IsRequired = false)]
        public string ApplicationData
        {
            get { return this.AzManApplication.ApplicationData; }
            set { this.AzManApplication.ApplicationData = value; }
        }

        internal IAzApplication3 AzManApplication
        {
            get;
            private set;
        }
        
        #endregion

        /// <summary>
        /// Returns Application ScopeCollection
        /// </summary>
        /// <returns>Application <see cref="ScopeCollection"/></returns>
        public ScopeCollection GetScopes()
        {
            return this.AzManApplication.Scopes != null ? new ScopeCollection(this, this.AzManApplication.Scopes) : null;
        }

        public bool ScopeExists(string scopeName)
        {
            if (string.IsNullOrEmpty(scopeName))
                return false;

            return this.AzManApplication.ScopeExists(scopeName);
        }

        //public bool OperationExists(string scopeName)
        //{
        //    return this.AzManApplication.Operations
        //}

        /// <summary>
        /// Criar <see cref="Task"/>
        /// </summary>
        /// <param name="scopeName">Name da <see cref="Task"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public Scope CreateScope(string scopeName)
        {
            var azmanScope = this.AzManApplication.CreateScope2(scopeName);
            return new Scope(this, azmanScope);
        }
        
        public void DeleteScope(string scopeName)
        {
            SecUtility.CheckParameter(ref scopeName, true, true, false, 64, "scopeName");
            this.DeleteAzScope(scopeName);
        }

        /// <summary>
        ///  Alters a <see cref="Scope"/> 
        /// </summary>
        /// <param name="scopeName">scope name</param>
        /// <param name="newScopeName">New operation name</param>
        /// <param name="newDescription">new scope description</param>
        /// <remarks>Case doesn't want to modify a attribute uses null</remarks>
        /// <returns>returns the modified<see cref="Operation"/></returns>
        public Scope UpdateScope(string scopeName, string newScopeName, string newDescription)
        {
            var scope  = this.GetScope(scopeName);

            if (scope != null)
            {
                if (newDescription != null)
                {
                    scope.Description = newDescription;
                }

                if (!string.IsNullOrEmpty(newScopeName))
                {
                    scope.Name = newScopeName;
                }
            }

            return scope;
        }

        /// <summary>
        /// Creates a <see cref="Task"/> in <see cref="Application"/> or in <see cref="Scope"/>
        /// </summary>
        /// <param name="name"><see cref="Task"/> name</param>
        /// <param name="description">The descriprion of the <see cref="Task"/></param>
        /// <returns><see cref="Task"/>The Task object with field filled by parameters</returns>
        public Scope CreateScope(string name, string description)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            var azmanScope = this.CreateAzScope(name);
            azmanScope.Name = name;
            azmanScope.Description = description;
            return new Scope(this, azmanScope);
        }

        /// <summary>
        /// Get <see cref="OperationCollection"/> from <see cref="Application"/>
        /// </summary>
        /// <returns></returns>
        public OperationCollection GetOperations()
        {
            return new OperationCollection(this.AzManApplication.Operations);
        }

        public string[] GetOperationNames()
        {
            var ops = this.AzManApplication.Operations;

            var names = new List<string>();

            foreach (IAzOperation2 operation in ops)
            {
                names.Add(operation.Name);
            }

            return names.ToArray();        
        }

        /// <summary>
        /// Obter <see cref="Operation"/> da <see cref="Application"/>
        /// </summary>
        /// <param name="operationName">Name da <see cref="Operation"/></param>
        /// <returns><see cref="Operation"/> da <see cref="Application"/></returns>
        public Operation GetOperation(string operationName)
        {
            try
            {
                var operation = this.AzManApplication.OpenOperation(operationName, null);
                return new Operation(operation as IAzOperation2);
            }
            catch (ExternalException ex)
            {
                var error =
                    new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ExternalError, ex);
                error.Raise();

                return null;
            }
        }

        /// <summary>
        /// Obter <see cref="TaskCollection"/> da <see cref="Application"/>
        /// </summary>
        /// <returns><see cref="TaskCollection"/> da <see cref="Application"/></returns>
        public TaskCollection GetTasks()
        {
            return this.GetTasks(null);
        }

        /// <summary>
        /// Obter <see cref="TaskCollection"/> da <see cref="Application"/> no <see cref="Scope"/>
        /// </summary>
        /// <param name="scopeName">Name do <see cref="Scope"/></param>
        /// <returns><see cref="TaskCollection"/> da <see cref="Application"/> no <see cref="Scope"/>/></returns>
        public TaskCollection GetTasks(string scopeName)
        {
            return new TaskCollection(this, this.GetAzTasks(scopeName), scopeName);
        }

        public string[] GetTaskNames(string scopeName)
        {
            IAzTasks tasks = this.GetAzTasks(scopeName);
            var names = new List<string>();

            foreach (IAzTask2 task in tasks)
            {
                if (task.IsRoleDefinition == 0)
                    names.Add(task.Name);
            }

            return names.ToArray();
        }

        /// <summary>
        ///  Retorna a <see cref="Task"/>
        /// </summary>
        /// <param name="taskName">Name Task</param>
        /// <returns>Retorna a classe <see cref="Task"/> obtida com o name</returns>
        public Task GetTask(string taskName)
        {
            return this.GetTask(taskName, null);
        }

        /// <summary>
        ///  Retorna a <see cref="Task"/>
        /// </summary>
        /// <param name="scopeName">Scope name</param>
        /// <param name="taskName">Task name</param>
        /// <returns>Retorna a classe <see cref="Task"/> obtida com o name</returns>
        public Task GetTask(string taskName, string scopeName)
        {
            return new Task(this, this.GetAzTask(taskName, scopeName), scopeName);
        }

        #region Roles

        /// <summary>
        /// Get <see cref="RoleCollection"/> from <see cref="Application"/>
        /// </summary>
        /// <returns><see cref="RoleCollection"/> from <see cref="Application"/></returns>
        public RoleCollection GetRoles()
        {
            return this.GetRoles(null);
        }

        public RoleCollection GetRoles(string scopeName)
        {
            return new RoleCollection(this, this.GetAzRoles(scopeName), scopeName);
        }

        public Role GetRole(string roleName)
        {
            return this.GetRole(roleName, null);
        }

        /// <summary>
        /// Obter <see cref="Role"/> da <see cref="Application"/> ou do <see cref="Scope"/>/>
        /// </summary>
        /// <param name="scopeName">The name of the <see cref="Scope"/>, null will search in root</param>
        /// <param name="roleName">The name of the <see cref="Role"/></param>
        /// <returns>The <see cref="Role"/> from the <see cref="Application"/></returns>
        public Role GetRole(string roleName, string scopeName)
        {
            return new Role(this, this.GetAzRoleAssignment(roleName, scopeName), scopeName);
        }

        public bool RoleAssignmentExists(string roleName, string scopeName)
        {
            try
            {
                return (this.GetAzRoleAssignment(roleName, scopeName) != null);
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return false;
            }
        }

        public bool RoleDefinitionExists(string roleDefinitionName, string scopeName)
        {
            try
            {
                return (this.GetAzRoleDefinition(roleDefinitionName, scopeName) != null);
            }
            catch (ExternalException ex)
            {
                var error =
                        new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return false;
            }
        }

        /// <summary>
        ///  Retorna name dos roles
        /// </summary>
        /// <returns>Name dos RoleCollection</returns>
        public string[] GetRoleAssignmentNames()
        {
            return this.GetRoleAssignmentNames(null);
        }

        /// <summary>
        ///  Retorna name dos roles
        /// </summary>
        /// <param name="scopeName">Name do Scope onde será feita a busca dos names dos roles</param>
        /// <returns>Name dos RoleCollection</returns>
        public string[] GetRoleDefinitionNames(string scopeName)
        {
            try
            {
                var roles = this.GetAzRoleDefinitions(scopeName);

                var roleNames = new string[roles.Count];

                int i = 0;
                foreach (IAzRoleDefinition role in this.AzManApplication.RoleDefinitions)
                {
                    roleNames[i++] = role.Name;
                }

                return roleNames;
            }
            catch (ExternalException ex)
            {
                var error =
                  new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                throw new AuthorizationException(ex, "IAzApplication2.OpenScope");
            }
        }

        /// <summary>
        ///  Return a collection with role assignment names
        /// </summary>
        /// <param name="scopeName">Name do Scope onde será feita a busca dos names dos roles</param>
        /// <returns>Name dos RoleCollection</returns>
        public string[] GetRoleAssignmentNames(string scopeName)
        { 
            try
            {
                var roles = this.GetAzRoleAssignments(scopeName);

                var roleNames = new string[roles.Count];

                int i = 0;
                foreach (IAzRoleAssignment role in this.AzManApplication.RoleAssignments)
                {
                    roleNames[i++] = role.Name;
                }

                return roleNames;
            }
            catch (ExternalException ex)
            {
                var error =
                      new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                throw new AuthorizationException(ex, "IAzApplication2.OpenScope");
            }
        }

        /// <summary>
        /// Criar <see cref="Task"/>
        /// </summary>
        /// <param name="name">Name da <see cref="Task"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public RoleDefinition CreateRoleDefinition(string name, string description, string scopeName)
        {
            var azmanRole = this.AzManApplication.CreateRoleDefinition(name);

            var role =  new RoleDefinition(this, azmanRole, scopeName) {Description = description};
            return role;
        }

        /// <summary>
        /// Criar <see cref="Task"/>
        /// </summary>
        /// <param name="name">Name da <see cref="Task"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public RoleAssignment CreateRoleAssignment(string name)
        {
            var azmanRole = this.AzManApplication.CreateRoleAssignment(name);
            return new RoleAssignment(this, azmanRole);
        }

        /// <summary>
        /// Criar a <see cref="Task"/> na <see cref="Application"/> ou no <see cref="Scope"/>
        /// </summary>
        /// <param name="name">Name da <see cref="Task"/></param>
        /// <param name="scopeName">Name do <see cref="Scope"/></param>
        /// <param name="description">Descrição da <see cref="Task"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public RoleAssignment CreateRoleAssignment(string name, string description, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            var azmanRole = this.CreateAzRoleAssignment(name, scopeName);
            azmanRole.Description = description;

            return new RoleAssignment(this, azmanRole);
        }

        public void DeleteRoleAssignment(string roleName, string scopeName)
        {
            SecUtility.CheckParameter(ref roleName, true, true, false, 64, "roleName");
            this.DeleteAzRoleAssignment(roleName, scopeName);
        }

        public void DeleteRoleDefinition(string roleDefinitionName, string scopeName)
        {
            SecUtility.CheckParameter(ref roleDefinitionName, true, true, false, 64, "roleDefinitionName");
            this.DeleteAzRoleDefinition(roleDefinitionName, scopeName);
        }

        public RoleAssignment UpdateRoleAssignment(string roleAssignmentName, string scopeName, string newRoleAssignmentName, string newDescription)
        {
            SecUtility.CheckParameter(ref roleAssignmentName, true, true, false, 64, "roleAssignmentName");

            var roleAssignment = this.GetRoleAssignment(roleAssignmentName, scopeName);

            if (roleAssignment != null)
            {
                if (newDescription != null)
                {
                    roleAssignment.Description = newDescription;
                }

                if (newRoleAssignmentName != null)
                {
                    roleAssignment.Name = newRoleAssignmentName;
                }
            }

            return roleAssignment;
        }

        public ApplicationGroup UpdateApplicationGroup(string applicationGroupName, string scopeName, string newApplicationGroupName, string newDescription)
        {
            SecUtility.CheckParameter(ref applicationGroupName, true, true, false, 64, "applicationGroupName");

            var applicationGroup = this.GetApplicationGroup(applicationGroupName, scopeName);

            if (applicationGroup != null)
            {
                if (newDescription != null)
                {
                    applicationGroup.Description = newDescription;
                }

                if (newApplicationGroupName != null)
                {
                    applicationGroup.Name = newApplicationGroupName;
                }
            }

            return applicationGroup;
        }

        public RoleDefinition UpdateRoleDefinition(string roleDefinitionName, string scopeName, string newRoleDefinitionName, string newDescription)
        {
            SecUtility.CheckParameter(ref roleDefinitionName, true, true, false, 64, "roleDefinitionName");

            var roleDefinition = this.GetRoleDefinition(roleDefinitionName, scopeName);

            if (roleDefinition != null)
            {
                if (newDescription != null)
                {
                    roleDefinition.Description = newDescription;
                }

                if (roleDefinitionName != null)
                {
                    roleDefinition.Name = newRoleDefinitionName;
                }
            }

            return roleDefinition;
        }

        #endregion

        /// <summary>
        ///  Retorna <see cref="AuthorizationContext"/>
        /// </summary>
        /// <param name="token"></param>
        /// <returns><see cref="AuthorizationContext"/> do usuário</returns>
        public AuthorizationContext GetAuthorizationContext(IntPtr token)
        {
            return new AuthorizationContext(this, token);
        }

        /// <summary>
        ///  Retorna <see cref="AuthorizationContext"/>
        /// </summary>
        /// <param name="sid">sid do usuário</param>
        /// <returns><see cref="AuthorizationContext"/> do usuário</returns>
        public AuthorizationContext GetAuthorizationContext(string sid)
        {
            return new AuthorizationContext(this, sid);
        }

        /// <summary>
        ///  Retorna <see cref="AuthorizationContext"/>
        /// </summary>
        /// <param name="sid">sid do usuário</param>
        /// <returns><see cref="AuthorizationContext"/> do usuário</returns>
        public AuthorizationContext GetAuthorizationContextByCustomSID(string[] SIDs)
        {
            return new AuthorizationContext(this, SIDs);
        }

        /// <summary>
        ///  Returns <see cref="AuthorizationContext"/>
        /// </summary>
        /// <param name="userName">User name</param>
        /// <param name="domainName">Domain name</param>
        /// <returns>User <see cref="AuthorizationContext"/></returns>
        public AuthorizationContext GetAuthorizationContext(string userName, string domainName)
        {
            return new AuthorizationContext(this, userName, domainName);
        }

        /// <summary>
        /// returns Application Scope
        /// </summary>
        /// <returns>Application <see cref="ScopeCollection"/></returns>
        public Scope GetScope(string scopeName)
        {
            return new Scope(this, this.AzManApplication.OpenScope2(scopeName));
        }

        /// <summary>
        /// Gets <see cref="Scope"/> names
        /// </summary>
        /// <returns>Lista com os names dos <see cref="ScopeCollection"/></returns>
        public string[] GetScopeNames()
        {
            var scopes = this.AzManApplication.Scopes;
            string[] scopeNames;

            if (scopes == null || scopes.Count == 0)
            {
                scopeNames = new[] { "" };
            }
            else
            {
                scopeNames = new string[scopes.Count];
                int i = 0;
                foreach (IAzScope scope in scopes)
                {
                    scopeNames[i++] = scope.Name;
                }
            }

            return scopeNames;
        }

        /// <summary>
        ///  Alters a <see cref="Operation"/> 
        /// </summary>
        /// <param name="operationName">operation name</param>
        /// <param name="newOperationName">New operation name</param>
        /// <param name="newDescription">new operation description</param>
        /// <param name="newOperationId">new operation id</param>
        /// <remarks>Case doesn't want to modify a attribute uses null or 0 in Id.</remarks>
        /// <returns>returns the modified<see cref="Operation"/></returns>
        public Operation UpdateOperation(string operationName, string newOperationName, string newDescription, int newOperationId)
        {
            SecUtility.CheckParameter(ref operationName, true, true, false, 64, "operationName");

            Operation operation = this.GetOperation(operationName);

            if (operation != null)
            {
                if (newDescription != null)
                {
                    operation.Description = newDescription;
                }

                if (newOperationName != null)
                {
                    operation.Name = newOperationName;
                }

                operation.OperationId = newOperationId == 0 ? operation.OperationId : newOperationId;
            }
            
            return operation;
        }

        /// <summary>
        /// Criar <see cref="Operation"/> na <see cref="Application"/>
        /// </summary>
        /// <param name="operationName">Name da <see cref="Operation"/></param>
        /// <returns><see cref="Operation"/> criada.</returns>
        public Operation CreateOperation(string operationName)
        {
            return  CreateOperation(-1,operationName,"");
        }

        /// <summary>
        /// Criar <see cref="Operation"/> na <see cref="Application"/>
        /// </summary>
        /// <param name="operationId"><see cref="Operation"/> operationId</param>
        /// <param name="name"><see cref="Operation"/> name</param>
        /// <param name="description"><see cref="Operation"/> description</param>
        /// <returns> the new <see cref="Operation"/>.</returns>
        public Operation CreateOperation(int operationId, string name, string description)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            var operacaoAZ = this.AzManApplication.CreateOperation(name, null);
            operacaoAZ.Description = description;      
            operacaoAZ.OperationID = operationId >= 0 ? operationId : GetNewOperationId();

            return new Operation(operacaoAZ as IAzOperation2);
        }

        private int GetNewOperationId()
        {

            var ids =  GetOperations().GetIds();
            int newId = 0;
            Array.Sort(ids);
           
            int i = 0;
            while (i <  ids.Length)
            {
                if (!ids.Contains(newId))
                    return newId;

                newId++;
                i++;
            }

            return newId;
        }

        /// <summary>
        /// Remover <see cref="Operation"/>
        /// </summary>
        /// <param name="operationName">Name da <see cref="Operation"/></param>
        public void DeleteOperation(string operationName)
        {
            this.AzManApplication.DeleteOperation(operationName, null);
        }

        /// <summary>
        ///  Create new a OperationCollection
        /// </summary>
        /// <param name="operationNames">Nomes das <see cref="OperationCollection"/></param>
        /// <returns><see cref="OperationCollection"/> da <see cref="Application"/></returns>
        public OperationCollection CreateOperations(string[] operationNames)
        {
            var operations = new OperationCollection();
            foreach (var operationName in operationNames)
            {
                operations.Add(this.CreateOperation(operationName));
            }

            return operations;
        }

        /// <summary>
        /// Creates an <see cref="ApplicationGroup"/> object with the specified name.
        /// </summary>
        /// <param name="name">Name for the new <see cref="ApplicationGroup"/> object.</param>
        /// <param name="description">A comment that describes the application group.</param>
        /// <param name="scopeName">The name of the <see cref="Scope"/> where the new object is definied. Null if it is definied in Application</param>
        /// <returns>The created <see cref="ApplicationGroup"/> object. </returns>
        /// <seealso cref="ApplicationGroup"/>
        public ApplicationGroup CreateApplicationGroup(string name, string description, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");
            var azmanApplicationGroup = this.CreateAzApplicationGroup(name, scopeName);
            azmanApplicationGroup.Description = this.Description;
            return new ApplicationGroup(this, azmanApplicationGroup, scopeName);
        }
       
        /// <summary>
        ///  Opens an <see cref="ApplicationGroup"/> object with the specified name.
        /// </summary>
        /// <param name="name">Name of the <see cref="ApplicationGroup"/> object to get.</param>
        /// <param name="scopeName">Scope name</param>
        /// <returns>The <see cref="ApplicationGroup"/> object</returns>
        /// <seealso cref="ApplicationGroup"/>
        public ApplicationGroupCollection GetApplicationGroups(string scopeName)
        {
            SecUtility.CheckParameter(ref scopeName, false, false, false, 64, "scopeName");
            try
            {
                var azmanAppGroups = this.GetAzApplicationGroups(scopeName);
                return new ApplicationGroupCollection(this, azmanAppGroups, scopeName);
            }
            catch (ExternalException  ex)
            {
                var error =
                    new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return null;
            }
        }

        /// <summary>
        /// Gets <see cref="Scope"/> names
        /// </summary>
        /// <returns>Lista com os names dos <see cref="ScopeCollection"/></returns>
        public string[] GetApplicationGroupNames(string scopeName)
        {

            SecUtility.CheckParameter(ref scopeName, false, false, false, 64, "scopeName");

            var appsGroups = this.GetAzApplicationGroups(scopeName);
            string[] groupNames;

            if (appsGroups == null || appsGroups.Count == 0)
            {
                groupNames = new[] { "" };
            }
            else
            {
                groupNames = new string[appsGroups.Count];
                int i = 0;
                foreach (IAzApplicationGroup2 group in appsGroups)
                {
                    groupNames[i++] = group.Name;
                }
            }

            return groupNames;
        }

        /// <summary>
        ///  Opens an <see cref="ApplicationGroup"/> object with the specified name.
        /// </summary>
        /// <param name="name">Name of the <see cref="ApplicationGroup"/> object to get.</param>
        /// <param name="scopeName">Scope name</param>
        /// <returns>The <see cref="ApplicationGroup"/> object</returns>
        /// <seealso cref="ApplicationGroup"/>
        public ApplicationGroup GetApplicationGroup(string name, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");
            var azmanApplicationGroup = this.AzManApplication.OpenApplicationGroup(name, null);
            return new ApplicationGroup(this, (IAzApplicationGroup2)azmanApplicationGroup, scopeName);
        }

        public RoleDefinitionCollection GetRoleDefinitions(string scopeName)
        {
            SecUtility.CheckParameter(ref scopeName, false, false, false, 64, "scopeName");
            try
            {
                var azmanRoleAssignment = this.GetAzRoleDefinitions(scopeName);
                return new RoleDefinitionCollection(this, azmanRoleAssignment, scopeName);
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return null;
            }
        }

        public bool ApplicationGroupExists(string applicationGroupName, string scopeName)
        {
            try
            {
                return (this.GetAzApplicationGroup(applicationGroupName, scopeName) != null);

            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return false;
            }
        }
         
        public RoleAssignmentCollection GetRoleAssignments(string scopeName)
        {
            SecUtility.CheckParameter(ref scopeName, false, false, false, 64, "scopeName");
            try
            {
                var azmanRoleAssignment = this.GetAzRoleAssignments(scopeName);
                return new RoleAssignmentCollection(this, azmanRoleAssignment);
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return null;
            }          
        }

        public RoleAssignment GetRoleAssignment(string name, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            try
            {
                IAzRoleAssignment azmanRoleAssignment = this.GetAzRoleAssignment(name, scopeName);
                return new RoleAssignment(this, azmanRoleAssignment);
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return null;
            }         
        }

        public RoleDefinition GetRoleDefinition(string name, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            try
            {
                IAzRoleDefinition azmanRoleDefinition = this.GetAzRoleDefinition(name, scopeName);
                return new RoleDefinition(this, azmanRoleDefinition, scopeName);
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return null;
            }
        }

        /// <summary>
        /// Removes the <see cref="ApplicationGroup"/> object with the specified name from the <see cref="ApplicationGroup"/> object.
        /// </summary>
        /// <param name="name">Name of the <see cref="ApplicationGroup"/> object to delete.</param>
        /// <seealso cref="ApplicationGroup"/>
        public void DeleteApplicationGroup(string name, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");
            DeleteAzApplicationGroup(name, scopeName);
        }
        
        /// <summary>
        /// Aceitar as alterações na <see cref="Application"/>
        /// </summary>
        public void Save()
        {
            this.AzManApplication.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
        }

        /// <summary>
        /// Abort all updates from <see cref="Application"/>
        /// </summary>
        public void Abort()
        {
            this.AzManApplication.Submit((int)tagAZ_PROP_CONSTANTS.AZ_SUBMIT_FLAG_ABORT, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
        }
   
        #region Tasks

        /// <summary>
        /// Criar <see cref="Task"/>
        /// </summary>
        /// <param name="taskName">Name da <see cref="Task"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public Task CreateTask(string taskName)
        {
            var azmanTask = this.AzManApplication.CreateTask(taskName, null);
            var task = new Task(this, (IAzTask2)azmanTask, null);
            return task;
        }

        /// <summary>
        /// Creates an IAzTask2 object with the specified name.
        /// </summary>
        /// <param name="name">Name for the new <see cref="Task"/> object.</param>
        /// <param name="roleDefinition">A value that indicates whether the task is a role definition.</param>
        /// <param name="scopeName">The name of the <see cref="Scope"/> where the new object is definied. Null if it is definied in Application</param>
        /// <param name="description">A comment that describes the task.</param>
        /// <returns>The created <see cref="Task"/> object.</returns>
        public Task CreateTask(string name, string description, bool roleDefinition, string scopeName)
        {
            SecUtility.CheckParameter(ref name, true, true, false, 64, "name");

            var azmanTask = this.CreateAzTask(name, scopeName);
            azmanTask.Name = name;
            azmanTask.Description = description;
            azmanTask.IsRoleDefinition = roleDefinition ? 1 : 0;

            return new Task(this, azmanTask, scopeName);
        }

        /// <summary>
        /// Criar a <see cref="Task"/> na <see cref="Application"/> ou no <see cref="Scope"/>
        /// </summary>
        /// <param name="task">Objeto <see cref="Task"/> com os dados da Task a ser criada.</param>
        /// <param name="scopeName">Name do <see cref="Scope"/></param>
        /// <returns><see cref="Task"/> criada</returns>
        public Task CreateTask(Task task, string scopeName)
        {
            if (task == null)
            {
                throw new ArgumentNullException("task");
            }

            var azmanTask = this.CreateAzTask(task.Name, scopeName);
            azmanTask.Name = task.Name;
            azmanTask.Description = task.Description;
            azmanTask.IsRoleDefinition = task.IsRoleDefinition ? 1 : 0;

            return new Task(this, azmanTask, scopeName);
        }

        public bool TaskExists(string taskName, string scopeName)
        {
            SecUtility.CheckParameter(ref taskName, true, true, false, 64, "taskName");
            try
            {
                var task = this.GetTask(taskName, scopeName);
                return task!=null;
            }
            catch (ExternalException ex)
            {
                var error =
                   new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.ApplicationError, ex);
                error.Raise();

                return false;
            }
        }

        public void DeleteTask(string taskName, string scopeName)
        {
            SecUtility.CheckParameter(ref taskName, true, true, false, 64, "taskName");
            this.DeleteAzTask(taskName, scopeName);
        }

        public void DeleteTask(Task task)
        {
            this.DeleteAzTask(task.Name, task.ScopeName);
        }

        public Task  UpdateTask(string taskName, string scopeName, string newTaskName, string newDescription)
        {
            SecUtility.CheckParameter(ref taskName, true, true, false, 64, "operationName");

            var task = this.GetTask(taskName);

            if (task != null)
            {
                if (newDescription != null)
                {
                    task.Description = newDescription;
                }

                if (newTaskName != null)
                {
                    task.Name = newTaskName;
                }
            }

            return task;
        }

     
        #endregion

        #region Private Methods

        /// <summary>
        ///  Retorna RoleCollection dos Azman
        /// </summary>
        /// <param name="scopeName">Nomes dos <see cref="ScopeCollection"/></param>
        /// <returns>RoleCollection dos Azman</returns>
        private IAzRoles GetAzRoles(string scopeName)
        {
            return String.IsNullOrEmpty(scopeName)
                                 ? this.AzManApplication.Roles
                                 : this.AzManApplication.OpenScope(scopeName, null).Roles;
        }

        /// <summary>
        ///  Returns TaskCollection from AzMan
        /// </summary>
        /// <param name="scopeName">Name do <see cref="Scope"/></param>
        /// <returns>TaskCollection do Azman</returns>
        private IAzTasks GetAzTasks(string scopeName)
        {
            return String.IsNullOrEmpty(scopeName)
                                 ? this.AzManApplication.Tasks
                                 : this.AzManApplication.OpenScope(scopeName, null).Tasks;
        }

        private IAzRoleDefinition GetAzRoleDefinition(string roleName, string scopeName)
        {

            return String.IsNullOrEmpty(scopeName)
                           ? this.AzManApplication.OpenRoleDefinition(roleName)
                           : this.AzManApplication.OpenScope2(scopeName).OpenRoleDefinition(roleName);
        }

        private IAzRoleDefinitions GetAzRoleDefinitions(string scopeName)
        {
            return String.IsNullOrEmpty(scopeName)
                               ? this.AzManApplication.RoleDefinitions
                               : this.AzManApplication.OpenScope2(scopeName).RoleDefinitions;
        }

        private IAzRoleAssignment GetAzRoleAssignment(string roleName, string scopeName)
        {
            return String.IsNullOrEmpty(scopeName) 
                            ? this.AzManApplication.OpenRoleAssignment(roleName)
                            : this.AzManApplication.OpenScope2(scopeName).OpenRoleAssignment(roleName);
        }

       private IAzRoleAssignments GetAzRoleAssignments(string scopeName)
       {
           return String.IsNullOrEmpty(scopeName)
                            ? this.AzManApplication.RoleAssignments
                            : this.AzManApplication.OpenScope2(scopeName).RoleAssignments;
       }

        /// <summary>
        ///  Retorna Task do Azman
        /// </summary>
        /// <param name="taskName">Name da <see cref="Task"/></param>
        /// <param name="scopeName">Name do <see cref="Scope"/></param>
        /// <returns>Task do Azman</returns>
        private IAzTask2 GetAzTask(string taskName, string scopeName)
        {
            return (String.IsNullOrEmpty(scopeName)
                        ? this.AzManApplication.OpenTask(taskName, null)
                        : this.AzManApplication.OpenScope(scopeName, null).OpenTask(taskName, null)) as IAzTask2;
        }

        private IAzApplicationGroup2 GetAzApplicationGroup(string applicationGroupName, string scopeName)
        {
            return (String.IsNullOrEmpty(scopeName)
                        ? this.AzManApplication.OpenApplicationGroup(applicationGroupName, null)
                        : this.AzManApplication.OpenScope2(scopeName).OpenApplicationGroup(applicationGroupName, null)) as IAzApplicationGroup2;
        }

        private IAzApplicationGroups GetAzApplicationGroups(string scopeName)
        {
            return String.IsNullOrEmpty(scopeName)
                              ? this.AzManApplication.ApplicationGroups
                              : this.AzManApplication.OpenScope2(scopeName).ApplicationGroups;
        }

        private IAzRoleAssignment CreateAzRoleAssignment(string name, string scopeName)
        {
            return String.IsNullOrEmpty(scopeName)
                              ? this.AzManApplication.CreateRoleAssignment(name)
                              : this.AzManApplication.OpenScope2(scopeName).CreateRoleAssignment(name);
        }

        private void DeleteAzApplicationGroup(string applicationGroupName, string scopeName)
        {
            if (String.IsNullOrEmpty(scopeName))
            {
                this.AzManApplication.DeleteApplicationGroup(applicationGroupName, null);
            }
            else
            {
                this.AzManApplication.OpenScope2(scopeName).DeleteApplicationGroup(applicationGroupName, null);
            }
        }

        private void DeleteAzRoleAssignment(string roleName, string scopeName)
        {
            if (String.IsNullOrEmpty(scopeName))
            {
                this.AzManApplication.DeleteRoleAssignment(roleName);
            }
            else
            {
                this.AzManApplication.OpenScope2(scopeName).DeleteRoleAssignment(roleName);
            }
        }

        private void DeleteAzRoleDefinition(string roleDefinitionName, string scopeName)
        {
            if (String.IsNullOrEmpty(scopeName))
            {
                this.AzManApplication.DeleteRoleDefinition(roleDefinitionName);
            }
            else
            {
                this.AzManApplication.OpenScope2(scopeName).DeleteRoleDefinition(roleDefinitionName);
            }
        }

        private IAzApplicationGroup2 CreateAzApplicationGroup(string applicationGroupName, string scopeName)
        {
            return (String.IsNullOrEmpty(scopeName)
                        ? this.AzManApplication.CreateApplicationGroup(applicationGroupName, null)
                        : this.AzManApplication.OpenScope(scopeName, null).CreateApplicationGroup(applicationGroupName, null)) as IAzApplicationGroup2;
        }

        private IAzTask2 CreateAzTask(string taskName, string scopeName)
        {
            return (String.IsNullOrEmpty(scopeName)
                        ? this.AzManApplication.CreateTask(taskName, null)
                        : this.AzManApplication.OpenScope2(scopeName).CreateTask(taskName, null)) as IAzTask2;
        }

        private void DeleteAzTask(string taskName, string scopeName)
        {
            if (String.IsNullOrEmpty(scopeName))
            {
                this.AzManApplication.DeleteTask(taskName, null);
            }
            else
            {
                this.AzManApplication.OpenScope2(scopeName).DeleteTask(taskName, null);
            }
        }

        private void DeleteAzScope(string scopeName)
        {
            this.AzManApplication.DeleteScope2(scopeName);
        }

        private IAzScope2 CreateAzScope(string name)
        {
            return this.AzManApplication.CreateScope2(name);
        }

        #endregion
       
    }
}